#include <cstddef>

#include <iostream>
#include <iomanip>

#include <thread>
#include <array>

#include "d2_thread_pool.hpp"

using namespace std;

atomic_int gIndex = 0;

mutex mutex_output;

auto memory_order_i = std::memory_order_relaxed;

std::ostream &oi(int i)
{
    std::cout << std::setw(3) << setfill('0') << i + 1 << ") -> ";
    return std::cout;
}

void empty()
{
    auto i = gIndex.fetch_add(1, memory_order_i);

    scoped_lock l(mutex_output);
    oi(i) << "empty\n";
}

struct MyException : std::runtime_error
{
    int value;

    explicit MyException(int v)
        : std::runtime_error(std::string("两数相加得到异常中奖值 ") + std::to_string(v)), value(v)
            
    {
    }

    ~MyException() noexcept override {}
};

// 有返回值
int add_two_maybe_exception(int a, int b)
{
    auto i = gIndex.fetch_add(1, memory_order_i);

    int r = a + b;

    if (r == 13)
    {
        throw MyException(r);
    }

    {
        scoped_lock l(mutex_output);
        oi(i) << a << "+" << b << " = " << r << "\n";
    }

    return r;
}

void output(char const *s)
{
    auto i = gIndex.fetch_add(1, memory_order_i);

    scoped_lock l(mutex_output);
    oi(i) << s << "\n";
}

void sleep(unsigned long ms)
{
    auto i = gIndex.fetch_add(1, memory_order_i);

    this_thread::sleep_for(chrono::milliseconds(ms));

    scoped_lock l(mutex_output);
    oi(i) << "sleep after " << ms << "\n";
}

struct Functor
{
    void operator()() const
    {
        auto i = gIndex.fetch_add(1, memory_order_i);

        {
            scoped_lock l(mutex_output);
            oi(i) << __FUNCTION__ << std::endl;
        }

        this_thread::sleep_for(chrono::milliseconds(100));
    }
};

int main()
{
    d2::ThreadPool pool;

    std::array<std::future<int>, 100> sumFutures;

    for (std::size_t i = 0; i < sumFutures.size(); i++)
    {
        pool.Submit(&::empty);

        sumFutures[i] = pool.Submit(&add_two_maybe_exception, i, i + 1);

        pool.Submit([]()
                    { ::output("How are you?"); });

        pool.Submit(&::empty);

        pool.Submit([]()
                    { ::output("I'm fine, and you?"); });

        pool.Submit(&::sleep, 490);

        pool.Submit(&::empty);

        pool.Submit(std::bind(&::sleep, 260));

        Functor f;
        pool.Submit(f);
    }

    int last = -1;
    do
    {
        int waitting = static_cast<int>(pool.GetPendingTaskCount());

        if (waitting != last)
        {
            scoped_lock l(mutex_output);
            std::cout << "等待中：" << waitting << "\n";
        }
        else
        {
            last = waitting;
        }

        if (!waitting)
        {
            break;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    while(true);
    

    pool.Stop();

    std::cout << "\n\n================\n\n";

    int sum = 0;
    for (auto &f : sumFutures)
    {
        try
        {
            sum += f.get();
        }
        catch (const MyException &e)
        {
            sum += e.value;
            std::cerr << e.what() << '\n';
        }
    }

    std::cout << "求两数最终累加结果：" << sum << std::endl;

    std::cout << "\n================\n\n";

    std::size_t total = 0;

    for (std::size_t i = 0; i < pool.GetSize(); ++i)
    {
        std::size_t c = pool.GetCompletedTaskCount(i);
        total += c;
        std::cout << "Thread " << i << " -> " << c << "\n";
    }

    std::cout << "累加后 => " << total << std::endl;
}